package jmine.tec.security.josso;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.josso.Lookup;
import org.josso.gateway.MutableSSOContext;
import org.josso.gateway.SSOContext;
import org.josso.gateway.SSOException;
import org.josso.gateway.SSOGateway;
import org.josso.gateway.SSORequestImpl;
import org.josso.gateway.SSOWebConfiguration;
import org.josso.gateway.assertion.AuthenticationAssertion;
import org.josso.gateway.session.SSOSession;
import org.josso.gateway.session.exceptions.NoSuchSessionException;
import org.josso.gateway.signon.Constants;

/**
 * Classe auxiliar para lidar com as funcoes do JOSSO
 * 
 * @author takeshi
 */
public final class JOSSOHelper {

    private static final Log LOGGER = LogFactory.getLog(JOSSOHelper.class);

    /**
     * 
     */
    private JOSSOHelper() {
    }

    /**
     * Devolve o {@link SSOGateway} do {@link ServletContext}
     * 
     * @param context {@link ServletContext}
     * @return {@link SSOGateway}
     */
    public static SSOGateway getSSOGateway(ServletContext context) {
        SSOGateway g = (SSOGateway) context.getAttribute(Constants.KEY_JOSSO_GATEWAY);
        if (g == null) {
            try {
                g = Lookup.getInstance().lookupSSOGateway();
                context.setAttribute(Constants.KEY_JOSSO_GATEWAY, g);
            } catch (Exception e) {
                throw new IllegalStateException("Cannot find the gateway", e);
            }
        }
        return g;
    }

    /**
     * Prepara o request, acertando contexto, back_to, etc
     * 
     * @param request o request
     * @param scheme nome do authentication scheme
     * @throws SSOException e
     */
    public static void prepareContext(HttpServletRequest request, String scheme) throws SSOException {
        // Use gateway to select a security domain
        SSOGateway gwy = getSSOGateway(request.getSession().getServletContext());

        // We need to store SSO parameters
        JOSSOHelper.storeSSOParameters(request);

        // The first thing to do is to create the context and publish the security domain !!!
        MutableSSOContext ctx = (MutableSSOContext) gwy.prepareSSOContext(new SSORequestImpl(request));
        ctx.setUserLocation(request.getRemoteHost());

        // Store current SD name in session
        request.getSession().setAttribute(org.josso.gateway.signon.Constants.KEY_JOSSO_SECURITY_DOMAIN_NAME,
                ctx.getSecurityDomain().getName());

        // SSO Session
        String sessionId = JOSSOHelper.getJossoSessionId(request);
        if (sessionId != null && !"".equals(sessionId)) {
            try {
                // If session is not valid, no current session will be available in context.
                ctx.setCurrentSession(gwy.findSession(sessionId));
            } catch (NoSuchSessionException e) {
                LOGGER.warn("cannot find session with id: " + sessionId, e);
            }
        }
        ctx.setScheme(scheme);
    }

    /**
     * This method stores SSO relevant request parameters as http session attributes.
     * 
     * @param request {@link HttpServletRequest}
     * @see #clearSSOParameters(javax.servlet.http.HttpServletRequest)
     * @see #PARAM_JOSSO_BACK_TO
     * @see #KEY_JOSSO_BACK_TO
     * @see #PARAM_JOSSO_ON_ERROR
     * @see #KEY_JOSSO_ON_ERROR
     * @see #KEY_JOSSO_SECURITY_DOMAIN_NAME
     */
    public static void storeSSOParameters(HttpServletRequest request) {

        // Get a session
        HttpSession s = request.getSession(true);

        // Store backTo url, if present.
        String backTo = request.getParameter(Constants.PARAM_JOSSO_BACK_TO);
        if (backTo != null && !"".equals(backTo)) {
            s.setAttribute(Constants.KEY_JOSSO_BACK_TO, backTo);
        }

        // Store on_error url if present.
        String onError = request.getParameter(Constants.PARAM_JOSSO_ON_ERROR);
        if (onError != null && !"".equals(onError)) {
            s.setAttribute(Constants.KEY_JOSSO_ON_ERROR, onError);
        }
    }

    /**
     * Clears SSO relevant attributes from http session.
     * 
     * @param req {@link HttpServletRequest}
     * @see storeSSOParameters
     */
    public static void clearSSOParameters(HttpServletRequest req) {
        final HttpSession session = req.getSession();
        session.removeAttribute(Constants.KEY_JOSSO_BACK_TO);
        session.removeAttribute(Constants.KEY_JOSSO_ON_ERROR);
        session.removeAttribute(Constants.KEY_JOSSO_SECURITY_DOMAIN_NAME);
    }

    /**
     * Devolve o back_to salvo na sessao
     * 
     * @param request {@link HttpServletRequest}
     * @param session {@link SSOSession}
     * @param authAssertion {@link AuthenticationAssertion}
     * @return String
     */
    public static String getBackTo(HttpServletRequest request, SSOSession session, AuthenticationAssertion authAssertion) {

        HttpSession httpSession = request.getSession();
        String backTo = (String) httpSession.getAttribute(Constants.KEY_JOSSO_BACK_TO);
        if (backTo == null) {
            try {
                SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
                backTo = cfg.getLoginBackToURL();
            } catch (Exception ex) {
                LOGGER.warn("Cannot lookup configuration", ex);
            }
        }

        if (backTo == null) {
            // Return to controller.
            return null;
        }

        backTo += (backTo.indexOf("?") >= 0 ? "&" : "?") + "josso_assertion_id=" + authAssertion.getId();

        return backTo;
    }

    /**
     * The 'backTo' url used when authentaction failed and the "" command was received
     * 
     * @param request the request
     * @return String
     */
    public static String getBackTo(HttpServletRequest request) {

        HttpSession httpSession = request.getSession();
        String backTo = (String) httpSession.getAttribute(Constants.KEY_JOSSO_BACK_TO);
        if (backTo == null) {
            try {
                SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();
                backTo = cfg.getLoginBackToURL();
            } catch (Exception ex) {
                LOGGER.warn("Cannot lookup configuration", ex);
            }
        }

        if (backTo == null) {
            LOGGER.warn("No 'BACK TO' URL received or configured ... using default forward rule !");
            return null;
        }

        return backTo;
    }

    /**
     * Procura no request o cookie de sessao setado pelo JOSSO
     * 
     * @param request {@link HttpServletRequest}
     * @param securityDomainName {@link String}
     * @return {@link Cookie} ou <code>null</code>
     */
    public static Cookie getJossoCookie(HttpServletRequest request, String securityDomainName) {
        return getCookie(request, org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + securityDomainName);
    }

    /**
     * Devolve o cookie com o nome passado
     * 
     * @param request {@link HttpServletRequest}
     * @param cookieName {@link String}
     * @return {@link Cookie}
     */
    public static Cookie getCookie(HttpServletRequest request, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }

        for (int i = 0; i < cookies.length; i++) {
            Cookie cookie = cookies[i];
            if (cookie.getName().equals(cookieName)) {
                return cookie;
            }
        }
        return null;

    }

    /**
     * Gets the josso session id value
     * <p/>
     * participantparam request
     * 
     * @param request the request
     * @return null, if JOSSO_SINGLE_SIGN_ON_COOKIE is not found in reqeust.
     */
    public static String getJossoSessionId(HttpServletRequest request) {
        SSOContext ctx = SSOContext.getCurrent();
        String jossoSessionId = null;

        try {
            SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();

            if (cfg.isSessionTokenOnClient()) {
                Cookie c = getJossoCookie(request, ctx.getSecurityDomain().getName());
                if (c != null) {
                    jossoSessionId = c.getValue();
                }
            } else {
                HttpSession session = request.getSession();
                return (String) session.getAttribute(org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE + "_"
                        + ctx.getSecurityDomain().getName());
            }
        } catch (Exception ex) {
            LOGGER.warn("Cannot lookup configuration", ex);
        }

        return jossoSessionId;
    }

    /**
     * Remove o JOSSO Session
     * 
     * @param request {@link HttpServletRequest}
     * @param response {@link HttpServletResponse}
     */
    public static void removeJossoSessionId(HttpServletRequest request, HttpServletResponse response) {
        SSOContext ctx = SSOContext.getCurrent();

        try {
            SSOWebConfiguration cfg = Lookup.getInstance().lookupSSOWebConfiguration();

            if (cfg.isSessionTokenOnClient()) {
                Cookie ssoCookie =
                        JOSSOHelper.newJossoCookie(request.getContextPath(), org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE + "_"
                                + ctx.getSecurityDomain().getName(), "-");
                ssoCookie.setMaxAge(0);
                response.addCookie(ssoCookie);
                ssoCookie =
                    JOSSOHelper.newJossoCookie(request.getContextPath(), org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE, "-");
            ssoCookie.setMaxAge(0);
            response.addCookie(ssoCookie);
            } else {
                HttpSession session = request.getSession();
                session.removeAttribute(org.josso.gateway.Constants.JOSSO_SINGLE_SIGN_ON_COOKIE + "_" + ctx.getSecurityDomain().getName());
            }

            if (cfg.isRememberMeEnabled()) {

                // Clear the remember me cookie
                Cookie rememberMeCookie =
                        new Cookie(org.josso.gateway.Constants.JOSSO_REMEMBERME_TOKEN + "_"
                                + SSOContext.getCurrent().getSecurityDomain().getName(), "-");
                rememberMeCookie.setMaxAge(0);
                rememberMeCookie.setSecure(cfg.isSessionTokenSecure());
                rememberMeCookie.setPath("/");

                response.addCookie(rememberMeCookie);
            }
        } catch (Exception ex) {
            LOGGER.warn("Cannot lookup configuration", ex);
        }
    }

    /**
     * Cria um cookie novo
     * 
     * @param path {@link String}
     * @param name {@link String}
     * @param value {@link String}
     * @return {@link Cookie}
     * @throws Exception se houver algum erro procurando a configuracao
     */
    public static Cookie newJossoCookie(String path, String name, String value) throws Exception {
        return newJossoCookie(Lookup.getInstance().lookupSSOWebConfiguration(), path, name, value);
    }

    /**
     * Cria um novo Cookie
     * 
     * @param cfg cfg
     * @param path path
     * @param name name
     * @param value value
     * @return Cookie
     */
    public static Cookie newJossoCookie(SSOWebConfiguration cfg, String path, String name, String value) {
        Cookie ssoCookie = new Cookie(name, value);
        ssoCookie.setMaxAge(-1);
        if (cfg.isSessionTokenSecure()) {
            ssoCookie.setSecure(true);
        }
        ssoCookie.setPath(path);
        return ssoCookie;
    }

}
